home *** CD-ROM | disk | FTP | other *** search
/ MacFormat España 14 / MacFormat n. 14 (Spain) / MacFormat 14.bin / Recursos Multimedia / Sounds / CD Reader / Code for reading digital audio < prev   
Encoding:
Internet Message Format  |  1994-01-13  |  31.9 KB

  1. Xref: uea.ac.uk comp.sys.mac.programmer:57152 alt.sources.mac:102
  2. Newsgroups: comp.sys.mac.programmer,alt.sources.mac
  3. Path: uea.ac.uk!doc.ic.ac.uk!agate!howland.reston.ans.net!europa.eng.gtefsd.com!uunet!EU.net!sun4nl!rulway.LeidenUniv.nl!ruls41.LeidenUniv.nl!vosse
  4. From: vosse@ruls41.LeidenUniv.nl (Theo Vosse)
  5. Subject: Code for reading digital audio with a CD300; here it is!
  6. Message-ID: <1994Jan13.122217.29702@rulway.LeidenUniv.nl>
  7. Sender: root@rulway.LeidenUniv.nl (System PRIVILEGED Account)
  8. Nntp-Posting-Host: ruls41.leidenuniv.nl
  9. Reply-To: vosse@ruls41.LeidenUniv.nl (Theo Vosse)
  10. Organization: Rijksuniversiteit Leiden
  11. Date: Thu, 13 Jan 94 12:22:17 GMT
  12. Lines: 1265
  13.  
  14. Hi.
  15.  
  16. As promised (in comp.sys.mac.programmer), my code to get digital audio
  17. directly from CD into your mac. Beware, you need an Apple CD 300!!!
  18. You can specify the section you want to download and in what format
  19. you want to save it (8/16 bit, mono/stereo, 11/22/44 kHz). The sound
  20. is always saved as a system 7 sound, which means that you need Sound
  21. Manager 3.0 to be able to play 16 bit sounds!
  22.  
  23. The code is intended for use with THINK C 5. I suppose you'll have to
  24. go through some trouble when you're using another compiler. The
  25. program is more or less (well, less) in mac style: initially, it shows
  26. you a dialog in which you can specify begin and end track and time,
  27. plus some conversion options. You need 176400 bytes of RAM for every
  28. second of digital audio you are downloading, even if you convert it
  29. directly to mono, 8 bits, 22kHz. The (dialog) resources are included
  30. at the end.
  31.  
  32. The code is pretty straightforward, although the function MaxVal(),
  33. used for normalization of the sound, contains some quirks: for some
  34. sounds it introduces an ugly clipping effect! 
  35.  
  36. Originally, I programmed some SCSI-routines to read the data (that was
  37. before I knew there was a cleaner way to do it with ToolBox calls).
  38. For completeness sake, I have included the SCSI parts at the end. They
  39. were partially derived from code by Thomas R. Shaw. The relevant
  40. information from the cdrom-faq is also included.
  41.  
  42. If you have any questions, comment or praising remarks, feel free to
  43. post them or mail them to me. Have fun!
  44.  
  45.     Theo Vosse
  46.     ----------
  47.     Unit for Experimental Psychology
  48.     University of Leiden
  49.     The Netherlands
  50.  
  51.  
  52. /* ----------------------------------------------------------------- */
  53. /* ReadAudioFromCD.c */
  54.  
  55. #include <stdio.h>
  56. #include <stdlib.h>
  57. #include <string.h>
  58. #include <stdarg.h>
  59. #include <Sound.h>
  60. #include <Sane.h>
  61. #include <Script.h>
  62. #include <Files.h>
  63. #include <StandardFile.h>
  64.  
  65. enum csCDCode
  66. {
  67.     csEjectTheDisc = 7,
  68.     csDiscStatus = 8,
  69.     csGetDriveType = 96,
  70.     csWhoIsThere = 97,
  71.     csReadTOC = 100,
  72.     csReadQ = 101,
  73.     csReadHeader = 102,
  74.     csReadMCN = 110,
  75.     csReadISRC = 111,
  76.     csReadAudio = 115
  77. };
  78.  
  79. enum csReadTOCSubCodes
  80. {
  81.     csReadTOCTrackCount = 1,
  82.     csReadTOCLeadOut = 2,
  83.     csReadTOCStartingAddress = 3,
  84.     csReadTOCTableOfContents = 4,
  85.     csReadTOCNrOfSessions = 5,
  86.     csReadTOCQSubCodes = 6
  87. };
  88.  
  89. enum csReadAudioCodes
  90. {
  91.     csReadAudioDataOnly = 0, /* 2352 bytes per block */
  92.     csReadAudioWithQSubCode = 1, /* 2368 bytes per block */
  93.     csReadAudioWithAllSubCodes = 2, /* 2448 bytes per block */
  94.     csReadAudioSubCodesOnly = 3 /* 96 bytes per block */
  95. };
  96.  
  97. enum AddressTypes
  98. {
  99.     logicalBlockAddressType = 0,
  100.     minSecFrameType = 1,
  101.     bcdTrackNumberType = 2
  102. };
  103.  
  104. enum DriveTypes
  105. {
  106.     AppleCDSC = 1,
  107.     AppleCDSCPlus150 = 2,
  108.     AppleCD300 = 3
  109. };
  110.  
  111. typedef struct StartingAddress
  112. {
  113.     Byte controlField;
  114.     Byte minutes; /* BCD */
  115.     Byte seconds; /* BCD */
  116.     Byte frames; /* BCD */
  117. } *StartingAddress;
  118.  
  119. typedef struct
  120. {
  121.     ParamBlockHeader
  122.     short int ioRefNum;
  123.     short int csCode;
  124.     union
  125.     {
  126.     struct
  127.     {
  128.         short int track;
  129.         Byte writeProtect;
  130.         Byte discStatus;
  131.         Byte installed;
  132.         Byte side;
  133.         long int qLink;
  134.         short int qType;
  135.         short int dQDrive;
  136.         short int dQRefNum;
  137.         short int dQFSID;
  138.         Byte twoSideFormat;
  139.         Byte needsFlush;
  140.         Byte diskErrs;
  141.     } discStatus;
  142.     struct
  143.     {
  144.         Byte fill;
  145.         Byte SCSIMask;
  146.     } showIsThere;
  147.     union
  148.     {
  149.         short int subType;
  150.         struct /* csReadTOCTrackCount */
  151.         {
  152.         Byte firstTrackNumber; /* BCD */
  153.         Byte lastTrackNumber; /* BCD */
  154.         } trackCount;
  155.         struct /* csReadTOCLeadOut */
  156.         {
  157.         Byte minutes; /* BCD */
  158.         Byte seconds; /* BCD */
  159.         Byte frames; /* BCD */
  160.         } leadOut;
  161.         struct /* csReadTOCStartingAddress */
  162.         {
  163.         short int subType;
  164.         StartingAddress buffer;
  165.         short int bufferSize;
  166.         Byte startingTrackNumber; /* BCD */
  167.         Byte fill;
  168.         } startingAddress;
  169.         struct /* csReadTOCTableOfContents */
  170.         {
  171.         short int subType;
  172.         void *buffer;
  173.         } tableOfContents;
  174.         struct /* csReadTOCNrOfSessions */
  175.         {
  176.         short int subType;
  177.         short int firstSessionNumber; /* BCD */
  178.         short int lastSessionNumber; /* BCD */
  179.         struct
  180.         {
  181.             Byte controlField;
  182.             Byte minutes; /* BCD */
  183.             Byte seconds; /* BCD */
  184.             Byte frames; /* BCD */
  185.         } firstTrackOfLastSession;
  186.         } nrOfSessions;
  187.         struct /* csReadTOCQSubCodes */
  188.         {
  189.         short int subType;
  190.         void *buffer;
  191.         short int bufferSize;
  192.         Byte startingSessionNumber; /* BCD */
  193.         Byte fill;
  194.         } qSubCode;
  195.     } readTOC;
  196.     struct
  197.     {
  198.         Byte mcValFound; /* MCVal found iff (mcValFound & 0x80) == 0x80 */
  199.         Byte mcn[15]; /* Media Catalog Number from MSB to LSB */
  200.     } readMCN;
  201.     struct
  202.     {
  203.         Byte tcValFound; /* ISRC data found iff (tcValFound & 0x80) == 0x80 */
  204.         Byte isrc[15]; /* Media Catalog Number from MSB to LSB */
  205.     } readISRC;
  206.     struct
  207.     {
  208.         short int audioType;
  209.         short int *buffer;
  210.         short int addressType;
  211.         union
  212.         {
  213.         long int logicalBlockAddress;
  214.         struct
  215.         {
  216.             Byte controlField;
  217.             Byte minutes; /* BCD */
  218.             Byte seconds; /* BCD */
  219.             Byte frames; /* BCD */
  220.         } startTime;
  221.         Byte trackNumber; /* BCD */
  222.         } address;
  223.         int numberOfFrames;
  224.         short int filler[4];
  225.     } readAudio;
  226.     short int driveType;
  227.     short int csParam[11];
  228.     } csParam;
  229. } CDIORec;
  230.  
  231. typedef union IntelWord
  232. {
  233.     Byte lh[2];
  234.     short int word;
  235. } IWrd;
  236.  
  237. static int nrTracks;
  238. static struct StartingAddress tracks[100];
  239. static int scsiID;
  240. static int driverRefNum;
  241. static int ioRefNum;
  242. static Handle sndHandle = NULL;
  243. static long int bufSize = 0;
  244. static int startTrack, startMinutes, startSeconds, startFrames,
  245.        endTrack,   endMinutes,   endSeconds,   endFrames;
  246. static OSErr mcnErr, isrcErr;
  247. static Boolean discPresent;
  248. static struct StartingAddress totalTime;
  249. static Byte mcn[15], isrc[15];
  250. static enum DriveTypes driveType;
  251. static unsigned int channels = 1, resolution = 8, sampleRate = 11025; 
  252. struct SndStruct
  253. {
  254.     unsigned int resource; /* Should be 1 */
  255.     struct SynthDef
  256.     {
  257.     short int nrOfSynths;
  258.     struct
  259.     {
  260.         short int resourceID;
  261.         long int initCmd;
  262.     } synth[1];
  263.     } synthDef;
  264.     unsigned int nrOfSoundCommands; /* Should be 1 */
  265.     struct SndCommand soundCommand;
  266.     Ptr data;
  267.     unsigned long int nrOfSamples; /* Or numChannels! */
  268.     Fixed sampleRate;
  269.     unsigned long int loopStart;
  270.     unsigned long int loopEnd;
  271.     unsigned char encode;
  272.     unsigned char baseNote;
  273.     struct ExtHeader
  274.     {
  275.     unsigned long numFrames;
  276.     extended80 AIFFSampleRate;
  277.     Ptr markerChunk;
  278.     Ptr instrumentChunks;
  279.     Ptr AESRecording;
  280.     unsigned short sampleSize;
  281.     unsigned short futureUse1;
  282.     unsigned long futureUse2;
  283.     unsigned long futureUse3;
  284.     unsigned long futureUse4;
  285.     } extHeader;
  286. } genHeader = {1, {1, {{sampledSynth, initMono}}},
  287.            1, {dataOffsetFlag | bufferCmd, 0, 0x14},
  288.            NULL, 0, 0x56EE8BA3, 0, 0, stdSH, 60};
  289. static int headerSize;
  290. static long int numFrames;
  291. static Boolean normalize = false;
  292.  
  293. static void aprintf(char *format, ...)
  294. {
  295.     va_list args;
  296.     char pText[256];
  297.  
  298.     va_start(args, format);
  299.     pText[0] = vsprintf(pText + 1, format, args);
  300.     va_end(args);
  301.     ParamText(pText, "\p", "\p", "\p");
  302.     Alert(128, NULL);
  303. }
  304.  
  305. static void dprintf(DialogPtr theDialog, int item, char *format, ...)
  306. {
  307.     va_list args;
  308.     char pText[256];
  309.     int itemType;
  310.     Handle itemHandle;
  311.     Rect itemRect;
  312.  
  313.     va_start(args, format);
  314.     pText[0] = vsprintf(pText + 1, format, args);
  315.     va_end(args);
  316.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  317.     SetIText(itemHandle, pText);
  318. }
  319.  
  320. static void dcontrol(DialogPtr theDialog, int item, int value)
  321. {
  322.     int itemType;
  323.     Handle itemHandle;
  324.     Rect itemRect;
  325.  
  326.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  327.     SetCtlValue(itemHandle, value);
  328. }
  329.  
  330. static long int dgetint(DialogPtr theDialog, int item)
  331. {
  332.     char pText[256];
  333.     int itemType;
  334.     Handle itemHandle;
  335.     Rect itemRect;
  336.     long int num;
  337.  
  338.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  339.     GetIText(itemHandle, pText);
  340.     StringToNum(pText, &num);
  341.     return (num);
  342. }
  343.  
  344. static void Signal(OSErr err)
  345. {
  346.     if (err != noErr)
  347.     {
  348.     aprintf("error %i occurred",err);
  349.     exit(1);
  350.     }
  351. }
  352.  
  353. static Byte Decimal2BCD(Byte n)
  354. {
  355.     return (((n / 10) << 4) + (n % 10));
  356. }
  357.  
  358. static Byte BCD2Decimal(Byte n)
  359. {
  360.     return (((n >> 4) * 10) + (n & 0x0f));
  361. }
  362.  
  363. static OSErr InitCD(void)
  364. {
  365.     return (OpenDriver("\p.AppleCD", &driverRefNum));
  366. }
  367.  
  368. static OSErr CDIO(CDIORec *cdIo)
  369. {
  370.     cdIo->ioCompletion = NULL;
  371.     cdIo->ioNamePtr = NULL;
  372.     cdIo->ioRefNum = ioRefNum;
  373.     return (PBControl((ParmBlkPtr) cdIo, false));
  374. }
  375.  
  376. static OSErr OpenCD(void)
  377. {
  378.     CDIORec whoIsThereRec;
  379.     OSErr err;
  380.  
  381.     whoIsThereRec.ioCompletion = NULL;
  382.     whoIsThereRec.ioNamePtr = NULL;
  383.     whoIsThereRec.ioRefNum = driverRefNum;
  384.     whoIsThereRec.csCode = csWhoIsThere;
  385.     if ((err = PBStatus((ParmBlkPtr) &whoIsThereRec, false)) != noErr)
  386.     {
  387.     return (err);
  388.     }
  389.     for (scsiID = 0; scsiID != 7; scsiID++)
  390.     {
  391.     /* Use lowest CD ID found */
  392.     if (whoIsThereRec.csParam.showIsThere.SCSIMask & (1 << scsiID))
  393.     {
  394.         ioRefNum = -(32 + scsiID) - 1;
  395.         return (noErr);
  396.     }
  397.     }
  398.     return (badUnitErr);
  399. }
  400.  
  401. /* Disc in Drive <=> discPresent */
  402. static OSErr DiscStatus(Boolean *discPresent)
  403. {
  404.     OSErr err;
  405.     CDIORec discStatusRec;
  406.  
  407.     discStatusRec.ioCompletion = NULL;
  408.     discStatusRec.ioNamePtr = NULL;
  409.     discStatusRec.ioRefNum = driverRefNum;
  410.     discStatusRec.csCode = csDiscStatus;
  411.     err = PBStatus((ParmBlkPtr) &discStatusRec, false);
  412.     *discPresent = discStatusRec.csParam.discStatus.discStatus == 1;
  413.     return (err);
  414. }
  415.  
  416. static OSErr DriveType(enum DriveTypes *driveType)
  417. {
  418.     CDIORec driveTypeRec;
  419.     OSErr err;
  420.  
  421.     driveTypeRec.ioCompletion = NULL;
  422.     driveTypeRec.ioNamePtr = NULL;
  423.     driveTypeRec.ioRefNum = driverRefNum;
  424.     driveTypeRec.csCode = csGetDriveType;
  425.     err = PBStatus((ParmBlkPtr) &driveTypeRec, false);
  426.     *driveType = driveTypeRec.csParam.driveType;
  427.     return (err);
  428. }
  429.  
  430. /* Gets number of tracks */
  431. static OSErr TrackCount(int *trackCount)
  432. {
  433.     OSErr err;
  434.     CDIORec readTOC;
  435.  
  436.     readTOC.csCode = csReadTOC;
  437.     readTOC.csParam.readTOC.subType = csReadTOCTrackCount;
  438.     if ((err = CDIO((CDIORec *) &readTOC)) == noErr)
  439.     {
  440.     *trackCount =
  441. BCD2Decimal(readTOC.csParam.readTOC.trackCount.lastTrackNumber) -
  442.               BCD2Decimal(readTOC.csParam.readTOC.trackCount.firstTrackNumber) + 1;
  443.     }
  444.     return (err);
  445. }
  446.  
  447. /* Info about total time */
  448. static OSErr TotalDiscTime(StartingAddress dTime)
  449. {
  450.     OSErr err;
  451.     CDIORec readTOC;
  452.  
  453.     readTOC.csCode = csReadTOC;
  454.     readTOC.csParam.readTOC.subType = csReadTOCLeadOut;
  455.     if ((err = CDIO(&readTOC)) == noErr)
  456.     {
  457.     dTime->minutes = BCD2Decimal(readTOC.csParam.readTOC.leadOut.minutes);
  458.     dTime->seconds = BCD2Decimal(readTOC.csParam.readTOC.leadOut.seconds);
  459.     dTime->frames = BCD2Decimal(readTOC.csParam.readTOC.leadOut.frames);
  460.     }
  461.     return (err);
  462. }
  463.  
  464. /* Info about track "track" */
  465. static OSErr TrackInfo(Byte track, StartingAddress dTime, int nrTracks)
  466. {
  467.     OSErr err;
  468.     CDIORec readTOC;
  469.     int i;
  470.  
  471.     readTOC.csCode = csReadTOC;
  472.     readTOC.csParam.readTOC.subType = csReadTOCStartingAddress;
  473.     readTOC.csParam.readTOC.startingAddress.buffer = dTime;
  474.     readTOC.csParam.readTOC.startingAddress.bufferSize = nrTracks *
  475. sizeof(struct StartingAddress);
  476.     readTOC.csParam.readTOC.startingAddress.startingTrackNumber =
  477. Decimal2BCD(track);
  478.     err = CDIO(&readTOC);
  479.     for (i = 0; i != nrTracks; i++)
  480.     {
  481.     dTime[i].minutes = BCD2Decimal(dTime[i].minutes);
  482.     dTime[i].seconds = BCD2Decimal(dTime[i].seconds);
  483.     dTime[i].frames = BCD2Decimal(dTime[i].frames);
  484.     }
  485.     return (err);
  486. }
  487.  
  488. static OSErr MediaCatalogNumber(Byte mcn[15])
  489. {
  490.     CDIORec mcnRec;
  491.     OSErr err;
  492.  
  493.     mcnRec.csCode = csReadMCN;
  494.     err = CDIO(&mcnRec);
  495.     memcpy(mcn, mcnRec.csParam.readMCN.mcn, 15);
  496.     return (err != noErr? err: (mcnRec.csParam.readMCN.mcValFound &
  497. 0x80) == 0x80? noErr: -1);
  498. }
  499.  
  500. static OSErr InternationalStandardRecordingCode(Byte isrc[15])
  501. {
  502.     CDIORec isrcRec;
  503.     OSErr err;
  504.  
  505.     isrcRec.csCode = csReadISRC;
  506.     err = CDIO(&isrcRec);
  507.     memcpy(isrc, isrcRec.csParam.readISRC.isrc, 15);
  508.     return (err != noErr? err: (isrcRec.csParam.readISRC.tcValFound &
  509. 0x80) == 0x80? noErr: -1);
  510. }
  511.  
  512. static OSErr EjectCD(void)
  513. {
  514.     OSErr err;
  515.     Str255 ioName;
  516.     HVolumeParam pb;
  517.  
  518.     pb.ioCompletion = NULL;
  519.     pb.ioNamePtr = ioName;
  520.     pb.ioVolIndex = 0;
  521.     do
  522.     {
  523.     pb.ioVolIndex++;
  524.     if ((err = PBHGetVInfo((HParmBlkPtr) &pb, false)) != noErr)
  525.     {
  526.         return (err);
  527.     }
  528.     }
  529.     while (pb.ioVDRefNum != ioRefNum);
  530.     if ((err = PBEject((ParmBlkPtr) &pb)) == noErr)
  531.     {
  532.     err = PBUnmountVol((ParmBlkPtr) &pb);
  533.     }
  534.     return (err);
  535. }
  536.  
  537. static OSErr ReadAudio(void *data, long int bufSize, int minutes, int
  538. seconds, int frames)
  539. {
  540.     CDIORec audioRec;
  541.     OSErr err;
  542.  
  543.     audioRec.csCode = csReadAudio;
  544.     audioRec.csParam.readAudio.audioType = csReadAudioDataOnly;
  545.     audioRec.csParam.readAudio.buffer = data;
  546.     audioRec.csParam.readAudio.addressType = minSecFrameType;
  547.     audioRec.csParam.readAudio.address.startTime.controlField = 0;
  548.     audioRec.csParam.readAudio.address.startTime.minutes =
  549. Decimal2BCD(minutes);
  550.     audioRec.csParam.readAudio.address.startTime.seconds =
  551. Decimal2BCD(seconds);
  552.     audioRec.csParam.readAudio.address.startTime.frames = Decimal2BCD(frames);
  553.     audioRec.csParam.readAudio.numberOfFrames = bufSize / 2352;
  554.     memset(audioRec.csParam.readAudio.filler, 0,
  555. sizeof(audioRec.csParam.readAudio.filler));
  556.     return (CDIO(&audioRec));
  557. }
  558.  
  559. static void ReadTime(void)
  560. {
  561.     long int t0;
  562.     OSErr err;
  563.  
  564.     startFrames += tracks[startTrack - 1].frames;
  565.     startSeconds += startFrames / 75 + tracks[startTrack - 1].seconds;
  566.     startFrames %= 75;
  567.     startMinutes += startSeconds / 60 + tracks[startTrack - 1].minutes;
  568.     startSeconds %= 60;
  569.     endFrames += tracks[endTrack - 1].frames;
  570.     endSeconds += endFrames / 75 + tracks[endTrack - 1].seconds;
  571.     endFrames %= 75;
  572.     endMinutes += endSeconds / 60 + tracks[endTrack - 1].minutes;
  573.     endSeconds %= 60;
  574.  
  575.     bufSize = (((long int) (endMinutes - startMinutes) * 60 +
  576.         endSeconds - startSeconds) * 75
  577.            + endFrames - startFrames)
  578.           * 2352L;
  579.     numFrames = bufSize / 4;
  580.     if ((sndHandle = NewHandle(bufSize + headerSize)) == NULL)
  581.     {
  582.     aprintf("Not enough memory");
  583.     exit(1);
  584.     }
  585.     HLock(sndHandle);
  586.  
  587.     t0 = TickCount();
  588.     err = ReadAudio(*sndHandle + headerSize, bufSize, startMinutes,
  589. startSeconds, startFrames);
  590.     t0 = TickCount() - t0;
  591.     if (err != noErr)
  592.     {
  593.     Signal(err);
  594.     }
  595.     aprintf("%ld bytes read in %g sec.", bufSize, t0 / 60.0);
  596. }
  597.  
  598. static Boolean GetTimes(void)
  599. {
  600.     DialogPtr timeDialog = GetNewDialog(128, NULL, (char *) -1);
  601.     int item;
  602.     int itemType;
  603.     Handle itemHandle;
  604.     Rect itemRect;
  605.  
  606.     if (timeDialog == NULL)
  607.     {
  608.     aprintf("???");
  609.     ExitToShell();
  610.     }
  611.     SetPort(timeDialog);
  612.     dprintf(timeDialog, 3, "Number of tracks: %d", nrTracks);
  613.     dprintf(timeDialog, 4, "Total playing time: %d:%02d:%02d",
  614.                 totalTime.minutes, totalTime.seconds, totalTime.frames);
  615.     if (mcnErr == noErr) dprintf(timeDialog, 5, "MCN: %15s", mcn);
  616.     else if (mcnErr == -1) dprintf(timeDialog, 5, "MCN: unknown");
  617.     else dprintf(timeDialog, 5, "MCN: error %d", mcnErr);
  618.     if (isrcErr == noErr) dprintf(timeDialog, 6, "ISRC: %15s", isrc);
  619.     else if (isrcErr == -1) dprintf(timeDialog, 6, "ISRC: unknown");
  620.     else dprintf(timeDialog, 6, "ISRC: error %d", isrcErr);
  621.     dcontrol(timeDialog, 21, 1);
  622.     dcontrol(timeDialog, 23, 1);
  623.     dcontrol(timeDialog, 25, 1);
  624.     dcontrol(timeDialog, 28, 0);
  625.     GetDItem(timeDialog, 1, &itemType, &itemHandle, &itemRect);
  626.     PenSize(3, 3); InsetRect(&itemRect, -4, -4);
  627.     FrameRoundRect(&itemRect, 16, 16);
  628.     do
  629.     {
  630.     ModalDialog(NULL, &item);
  631.     if (item == 21 || item == 22)
  632.     {
  633.         channels = (item == 21? 1: 2);
  634.         dcontrol(timeDialog, 21, channels == 1);
  635.         dcontrol(timeDialog, 22, channels == 2);
  636.     }
  637.     else if (item == 23 || item == 24)
  638.     {
  639.         resolution = (item == 23? 8: 16);
  640.         dcontrol(timeDialog, 23, resolution == 8);
  641.         dcontrol(timeDialog, 24, resolution == 16);
  642.     }
  643.     else if (25 <= item && item <= 27)
  644.     {
  645.         sampleRate = (item == 25? 11025: (item == 26? 22050: 44100));
  646.         dcontrol(timeDialog, 25, sampleRate == 11025);
  647.         dcontrol(timeDialog, 26, sampleRate == 22050);
  648.         dcontrol(timeDialog, 27, sampleRate == 44100);
  649.     }
  650.     else if (item == 28)
  651.     {
  652.         normalize = !normalize;
  653.         dcontrol(timeDialog, 28, normalize);
  654.     }
  655.     }
  656.     while (item != 1 && item != 2);
  657.     startTrack = dgetint(timeDialog, 13);
  658.     startMinutes = dgetint(timeDialog, 14);
  659.     startSeconds = dgetint(timeDialog, 15);
  660.     startFrames = dgetint(timeDialog, 16);
  661.     endTrack = dgetint(timeDialog, 17);
  662.     endMinutes = dgetint(timeDialog, 18);
  663.     endSeconds = dgetint(timeDialog, 19);
  664.     endFrames = dgetint(timeDialog, 20);
  665.     if (channels == 1 && resolution == 8)
  666.     {
  667.     headerSize = sizeof(struct SndStruct) - sizeof(struct ExtHeader);
  668.     }
  669.     else
  670.     {
  671.     headerSize = sizeof(struct SndStruct);
  672.     }
  673.     DisposDialog(timeDialog);
  674.     return (item == 1);
  675. }
  676.  
  677. static long int MaxVal(void)
  678. {
  679.     long int length = bufSize;
  680.     IWrd *lChan = (IWrd *) (*sndHandle + headerSize);
  681.     IWrd *rChan = lChan + 1;
  682.     long int lDatum, rDatum;
  683.     int nrSamples = (sampleRate == 11025? 4: sampleRate == 22050? 2: 1);
  684.     int i;
  685.     Byte swap;
  686.     long int max = 0;
  687.  
  688.     while (length > 0)
  689.     {
  690.     lDatum = rDatum = 0;
  691.     for (i = 0; i != nrSamples; i++)
  692.     {
  693.         swap = lChan->lh[0]; lChan->lh[0] = lChan->lh[1]; lChan->lh[1] = swap;
  694.         swap = rChan->lh[0]; rChan->lh[0] = rChan->lh[1]; rChan->lh[1] = swap;
  695.         lDatum += lChan->word; rDatum += rChan->word;
  696.         lChan += 2; rChan += 2; length -= 4;
  697.     }
  698.     if (channels == 1)
  699.     {
  700.         lDatum = (lDatum + rDatum) / 2;
  701.         if (lDatum < 0) lDatum = -lDatum;
  702.         if (lDatum > max) max = lDatum;
  703.     }
  704.     else
  705.     {
  706.         if (lDatum < 0) lDatum = -lDatum;
  707.         if (lDatum > max) max = lDatum;
  708.         if (rDatum < 0) rDatum = -rDatum;
  709.         if (rDatum > max) max = rDatum;
  710.     }
  711.     }
  712.     return (max);
  713. }
  714.  
  715. static void ConvertSound(void)
  716. {
  717.     char *bPtr = *sndHandle + headerSize;
  718.     short int *wPtr = (short int *) bPtr;
  719.     long int length = bufSize;
  720.     IWrd *lChan = (IWrd *) bPtr;
  721.     IWrd *rChan = lChan + 1;
  722.     long int lDatum, rDatum;
  723.     int nrSamples = (sampleRate == 11025? 4: sampleRate == 22050? 2: 1);
  724.     int i;
  725.     Byte swap;
  726.     long int max = (normalize? MaxVal(): 32768);
  727.  
  728.     while (length > 0)
  729.     {
  730.     lDatum = rDatum = 0;
  731.     for (i = 0; i != nrSamples; i++)
  732.     {
  733.         if (!normalize)
  734.         {
  735.         swap = lChan->lh[0]; lChan->lh[0] = lChan->lh[1]; lChan->lh[1] = swap;
  736.         swap = rChan->lh[0]; rChan->lh[0] = rChan->lh[1]; rChan->lh[1] = swap;
  737.         }
  738.         lDatum += lChan->word; rDatum += rChan->word;
  739.         lChan += 2; rChan += 2; length -= 4;
  740.     }
  741.     if (normalize)
  742.     {
  743.         lDatum = lDatum * 32767 / max;
  744.         rDatum = rDatum * 32767 / max;
  745.     }
  746.     else
  747.     {
  748.         lDatum /= nrSamples;
  749.         rDatum /= nrSamples;
  750.     }
  751.     if (channels == 1)
  752.     {
  753.         lDatum = (lDatum + rDatum) / 2;
  754.     }
  755.     if (resolution == 8)
  756.     {
  757.         *bPtr++ = (Byte) (lDatum >> 8) ^ 0x80;
  758.     }
  759.     else
  760.     {
  761.         *wPtr++ = lDatum;
  762.     }
  763.     if (channels == 2)
  764.     {
  765.         if (resolution == 8)
  766.         {
  767.         *bPtr++ = (rDatum >> 8) ^ 0x80;
  768.         }
  769.         else
  770.         {
  771.         *wPtr++ = rDatum;
  772.         }
  773.     }
  774.     }
  775.     HUnlock(sndHandle);
  776.     SetHandleSize(sndHandle, headerSize + resolution / 8 * channels *
  777. numFrames);
  778.     Signal(ResError());
  779. }
  780.  
  781. static void InitHeader(void)
  782. {
  783.     long double dSampleRate = (double) sampleRate;
  784.  
  785.     numFrames = numFrames / (44100 / (long unsigned int) sampleRate);
  786.     genHeader.sampleRate = (long unsigned int) sampleRate << 16;
  787.     if (channels == 1 && resolution == 8)
  788.     {
  789.     genHeader.nrOfSamples = numFrames;
  790.     }
  791.     else
  792.     {
  793.     if (channels == 2)
  794.     {
  795.         genHeader.synthDef.synth[0].initCmd = initStereo;
  796.     }
  797.     genHeader.nrOfSamples = channels;
  798.     genHeader.encode = extSH;
  799.     genHeader.extHeader.numFrames = numFrames;
  800.     x96tox80(&dSampleRate, &genHeader.extHeader.AIFFSampleRate);
  801.     genHeader.extHeader.markerChunk = NULL;
  802.     genHeader.extHeader.instrumentChunks = NULL;
  803.     genHeader.extHeader.AESRecording = NULL;
  804.     genHeader.extHeader.sampleSize = resolution;
  805.     genHeader.extHeader.futureUse1 = 0;
  806.     genHeader.extHeader.futureUse2 = 0;
  807.     genHeader.extHeader.futureUse3 = 0;
  808.     genHeader.extHeader.futureUse4 = 0;
  809.     }
  810.     memcpy((void *) *sndHandle, &genHeader, headerSize);
  811. }
  812.  
  813. static void SaveSound(void)
  814. {
  815.     StandardFileReply reply;
  816.     int refNum;
  817.  
  818.     StandardPutFile("\pWhere to put file", "\pSound", &reply);
  819.     if (reply.sfGood)
  820.     {
  821.     if (reply.sfReplacing)
  822.     {
  823.         FSpDelete(&reply.sfFile);
  824.     }
  825.     FSpCreateResFile(&reply.sfFile, 'movr', 'sfil', smSystemScript);
  826.     Signal(ResError());
  827.     refNum = FSpOpenResFile(&reply.sfFile, fsWrPerm);
  828.     if (refNum == -1)
  829.     {
  830.         Signal(ResError());
  831.     }
  832.     AddResource(sndHandle, 'snd ', 128, reply.sfFile.name);
  833.     CloseResFile(refNum);
  834.     }
  835. }
  836.  
  837. static void SetupCD(void)
  838. {
  839.     /* Initialize CD */
  840.     Signal(InitCD());
  841.     Signal(OpenCD());
  842.  
  843.     /* Check for drive */
  844.     Signal(DriveType(&driveType));
  845.     if (driveType < AppleCD300)
  846.     {
  847.     aprintf("Improper drive");
  848.     }
  849.  
  850.     /* Check if present and of proper type */
  851.     Signal(DiscStatus(&discPresent));
  852.     if (!discPresent)
  853.     {
  854.     aprintf("No disc present");
  855.     ExitToShell();
  856.     }
  857.  
  858.     /* Get CD info */
  859.     Signal(TrackCount(&nrTracks));
  860.     Signal(TotalDiscTime(&totalTime));
  861.  
  862.     /* Get info of all tracks */
  863.     Signal(TrackInfo(1, tracks, nrTracks));
  864.  
  865.     /* Check for Media Catalog Number */
  866.     mcnErr = MediaCatalogNumber(mcn);
  867.  
  868.     /* Check for International Standard Recording Code */
  869.     isrcErr = InternationalStandardRecordingCode(isrc);
  870. }
  871.  
  872. void main(void)
  873. {
  874.     InitGraf(&thePort);
  875.     InitFonts();
  876.     FlushEvents(everyEvent, 0);
  877.     InitWindows();
  878.     InitMenus();
  879.     TEInit();
  880.     InitDialogs(NULL);
  881.     InitCursor();
  882.  
  883.     SetupCD();
  884.     if (GetTimes())
  885.     {
  886.     ReadTime();
  887.     InitHeader();
  888.     ConvertSound();
  889.     SaveSound();
  890.     }
  891. }
  892.  
  893. /* ----------------------------------------------------------------- */
  894. /* ReadAudioFromCD.r: compile with SARez */
  895.  
  896. data 'ALRT' (128) {
  897.     $"0048 0034 00A7 0123 0080 5555 300A"
  898. };
  899.  
  900. data 'DITL' (128) {
  901.     $"0002 0000 0000 003C 005A 0050 0094 0404"
  902.     $"416C 6173 0000 0000 000A 000A 002A 002A"
  903.     $"A002 0000 0000 0000 000A 0032 002A 00DC"
  904.     $"8802 5E30"
  905. };
  906.  
  907. data 'DITL' (129) {
  908.     $"001B 0000 0000 0057 0118 006B 0152 0402"
  909.     $"4F6B 0000 0000 0076 0118 008A 0152 0406"
  910.     $"4361 6E63 656C 0000 0000 000A 000A 001A"
  911.     $"0098 8800 0000 0000 000A 00A0 001A 015B"
  912.     $"8800 0000 0000 001E 000A 002D 0131 8800"
  913.     $"0000 0000 0032 000A 0041 0131 8800 0000"
  914.     $"0000 005A 000A 006A 0031 8804 4672 6F6D"
  915.     $"0000 0000 0046 0032 0056 0058 8805 5472"
  916.     $"6163 6B00 0000 0000 0077 000A 0087 0022"
  917.     $"8802 546F 0000 0000 0046 0064 0056 0096"
  918.     $"8806 4D69 6E75 7465 0000 0000 0046 009B"
  919.     $"0056 00CF 8806 5365 636F 6E64 0000 0000"
  920.     $"0046 00D2 0056 0102 8805 4672 616D 6500"
  921.     $"0000 0000 005A 0033 006B 005A 1000 0000"
  922.     $"0000 005A 0069 006B 0090 1000 0000 0000"
  923.     $"005A 00A0 006B 00C7 1000 0000 0000 005A"
  924.     $"00D5 006B 00FC 1000 0000 0000 0076 0033"
  925.     $"0087 005A 1000 0000 0000 0076 0069 0087"
  926.     $"0090 1000 0000 0000 0076 00A0 0087 00C7"
  927.     $"1000 0000 0000 0076 00D5 0087 00FC 1000"
  928.     $"0000 0000 0096 000A 00A8 0048 0604 4D6F"
  929.     $"6E6F 0000 0000 00AA 000A 00BC 004C 0606"
  930.     $"5374 6572 656F 0000 0000 0096 0064 00A8"
  931.     $"0098 0605 3820 6269 7400 0000 0000 00AA"
  932.     $"0064 00BD 00A0 0606 3136 2062 6974 0000"
  933.     $"0000 0096 00B4 00A8 00FC 0606 3131 206B"
  934.     $"487A 0000 0000 00AA 00B4 00BC 00FC 0606"
  935.     $"3232 206B 487A 0000 0000 00BE 00B4 00D0"
  936.     $"00FC 0606 3434 206B 487A 0000 0000 00BE"
  937.     $"000A 00D0 0074 0509 4E6F 726D 616C 697A"
  938.     $"6500"
  939. };
  940.  
  941. data 'DLOG' (128) {
  942.     $"003C 0020 0116 017D 0005 0100 0000 0000"
  943.     $"0000 0081 00"
  944. };
  945.  
  946. /* ----------------------------------------------------------------- */
  947. /* older SCSI parts */
  948.  
  949. /*From the CDROM FAQ:
  950.   There are several ways to read digital audio from Sony CDU 561
  951.   and Sony CDU 8003 mechanisms. Note that the technique of merely
  952.   setting the density (0x82) using MODE SELECT SCSI command as on
  953.   Toshiba 3401s will not work.
  954.  
  955.   Here are three ways to read digital audio Red Book standard
  956.   audio track data across the SCSI bus into your computer complete
  957.   with all sound processing already performed (For example the
  958.   CIRC routine already run and the output is LRLRLR pairs of 16
  959.   bit digital audio samples 2352 bytes per CD-ROM block.
  960.  
  961.   Method 1 : READ CD-DA scsi command 0xD8
  962.  
  963.   Byte 0: D8
  964.   1: <LUN stuff> 0
  965.   2: <4th most significant byte of logical block address>
  966.   3: <3rd byte>
  967.   4: <2nd>
  968.   5: <1st, lowest of the address>
  969.   6: <4th most significant byte of transfer length
  970.   7: <3rd byte>
  971.   8: <2nd>
  972.   9: <1st, lowest of the number of contiguos blocks to transfer>
  973.   10: <special sub code selector> (0 == normal 2352, other values
  974.       are 01, 02, 03)
  975.   11: <control>
  976.    
  977.   Method 2 : READ CD-DA MSF scsi command 0xD9
  978.    
  979.   byte 0: D9
  980.   1: <LUN stuff> 0
  981.   2: 0
  982.   3: <starting minute in binary not BCD>
  983.   4: <starting second in binary not BCD>
  984.   5: <starting frame (75th of a second) in binary not BCD>
  985.   6: 0
  986.   7: <ending minute in binary not BCD>
  987.   8: <ending second in binary not BCD>
  988.   9: <ending frame (75th of a second) in binary not BCD>
  989.   10: <special sub code selector> (0 == normal 2352 each, other
  990.       values are 01, 02, 03)
  991.   11: <control>
  992.    
  993.   For this one you will need to remember how to convert MSF to
  994.   logical (LBA) address to set the SCSI transfer length correctly
  995.   to avoid the Mac SCSI manager reporting a phase error. to
  996.   calculate the number of bytes total you will get use the formula:
  997.  
  998.   ((Me-Ms)*60*75 + (Se-Ss) * 75 + (Fe-Fs)) * (2352) */
  999.  
  1000. #include <stdio.h>
  1001. #include <stdlib.h>
  1002. #include <string.h>
  1003. #include <SCSI.h>
  1004.  
  1005. #define csDiscStatus 8
  1006. #define csWhoIsThere 97
  1007. #define csReadTOC 100
  1008. #define csReadTOCTrackCount 1
  1009. #define csReadTOCEndOfDisk 2
  1010. #define csReadTOCStartTime 2
  1011. #define csReadQ 101
  1012.  
  1013. #define scsiReadXfer 1
  1014. #define scsiWriteXfer -1
  1015. #define scsiNoXfer 0
  1016.  
  1017. #define kSCSIStatusMask 0x1f
  1018.  
  1019. #define kGood 0x00
  1020. #define kCheckCondition 0x02
  1021. #define kBusy 0x08
  1022. #define kReservationConflict 0x18
  1023.  
  1024. #define kSCSIRetryValue 5
  1025.  
  1026. #define bLogicalUnitLSB 5
  1027.  
  1028. #define kCommandComplete 0x00
  1029. #define kExtendedMessage 0x01
  1030. #define kSaveDataPointer 0x02
  1031. #define kRestorePointers 0x03
  1032. #define kDisconnect 0x04
  1033. #define kMessageReject 0x07
  1034. #define kLinkedCommandComplete 0x0a
  1035. #define kLinkedCommandCompleteWithFlags 0x0b
  1036.  
  1037. #define DoSCSISenseUnit(TargetSCSIID, LogicalUnit) -1 /*
  1038. FindError(TargetSCSIID) */
  1039.  
  1040. enum
  1041. {
  1042.     eSCSIReservationConflict = 0xf000,
  1043.     eSCSIBusy,
  1044.     eDisconnect,
  1045.     eMessageReject,
  1046.     eReplyTOErr
  1047. };
  1048.  
  1049. static struct
  1050. {
  1051.     Byte command; /* D9 */
  1052.     Byte lun; /* 0 */
  1053.     Byte zero1; /* 0 */
  1054.     Byte startMinute; /* Not in BCD */
  1055.     Byte startSecond;
  1056.     Byte startFrame;
  1057.     Byte zero2; /* 0 */
  1058.     Byte endMinute; /* Not in BCD */
  1059.     Byte endSecond;
  1060.     Byte endFrame;
  1061.     Byte subCodeSelector; /* 0 == normal 2352 each, other values are 01,
  1062. 02, 03 */
  1063.     Byte control; /* ??? */
  1064. } readCommand1 =
  1065. {
  1066.     0xD9, 0x00, 0x00,
  1067.     0x01, 0x02, 0x03,
  1068.     0x00, 0x01, 0x02,
  1069.     0x04, 0x00, 0x00
  1070. };
  1071.  
  1072. static OSErr DoSCSIOutput(short TargetSCSIID, short scsiCmdSize, void *scsiCmd,
  1073.               SCSIInstr *scsiInst, short RdWrNone, long TimeOut)
  1074. {
  1075.     short ReturnedSCSIStat;
  1076.     short ReturnedSCSIMessage;
  1077.     short NumTries = 0;
  1078.     short stat;
  1079.     OSErr err;
  1080.     OSErr err1;
  1081.     short i;
  1082.     short message;
  1083.     
  1084.     while (NumTries++ < kSCSIRetryValue) 
  1085.     {
  1086.     err = SCSIGet();
  1087.     if (err == noErr) 
  1088.     {
  1089.         err = SCSISelect(TargetSCSIID);
  1090.         if (err == noErr) 
  1091.         {
  1092.         err = SCSICmd(scsiCmd, scsiCmdSize);
  1093.         if (err == noErr) 
  1094.         {
  1095.             stat = SCSIStat();
  1096.             switch (RdWrNone) 
  1097.             {
  1098.             case scsiReadXfer:
  1099.                 err = SCSIRead((Ptr) scsiInst);
  1100.                 break;
  1101.             case scsiWriteXfer:
  1102.                 err = SCSIWrite((Ptr) scsiInst);
  1103.                 break;
  1104.             }
  1105.         }
  1106.         err1 = SCSIComplete(&ReturnedSCSIStat, &ReturnedSCSIMessage, TimeOut);
  1107.         if (err == noErr) 
  1108.         {
  1109.             if (err1 == noErr) 
  1110.             {
  1111.             switch (ReturnedSCSIStat & kSCSIStatusMask) 
  1112.             {
  1113.                 case kGood:
  1114.                 err = noErr;
  1115.                 break;
  1116.              /* case kConditionMetGood:
  1117.                 err = noErr;
  1118.                 break;
  1119.                 case kIntermediateGood:
  1120.                 err = noErr;
  1121.                 break;
  1122.                 case kIntermediateMetGood:
  1123.                 err = noErr;
  1124.                 break; */
  1125.                 case kReservationConflict:
  1126.                 err = eSCSIReservationConflict;
  1127.                 break;
  1128.                 case kBusy:
  1129.                 err = eSCSIBusy;
  1130.                 break;
  1131.                 case kCheckCondition:
  1132.                 switch (ReturnedSCSIMessage) 
  1133.                 {
  1134.                     case kCommandComplete:
  1135.                     case kLinkedCommandCompleteWithFlags:
  1136.                     case kLinkedCommandComplete:
  1137.                     err = DoSCSISenseUnit(TargetSCSIID,
  1138.                                   scsiCmd->LUN_Res >> bLogicalUnitLSB);
  1139.                     break;
  1140.                     case kExtendedMessage:
  1141.                     if (SCSIMsgIn(&message) == noErr) 
  1142.                     {
  1143.                         for (i = 0; i < message; i++) 
  1144.                         {
  1145.                         if (SCSIMsgIn(&message) != noErr)
  1146.                         {
  1147.                             break;
  1148.                         }
  1149.                         }
  1150.                     }
  1151.                     break;
  1152.                     case kDisconnect:
  1153.                     err = eDisconnect;
  1154.                     break;
  1155.                     case kMessageReject:
  1156.                     err = eMessageReject;
  1157.                     break;
  1158.                     default:
  1159.                     if (ReturnedSCSIMessage >= 0x80) 
  1160.                     {
  1161.                         err = noErr;
  1162.                     } 
  1163.                     else 
  1164.                     {
  1165.                         err = DoSCSISenseUnit(TargetSCSIID,
  1166.                                   scsiCmd->LUN_Res >> bLogicalUnitLSB);
  1167.                     }
  1168.                     break;
  1169.                 }
  1170.                 break;
  1171.             }
  1172.             } 
  1173.             else 
  1174.             {
  1175.             err = err1;
  1176.             }
  1177.         }
  1178.         break;
  1179.         }
  1180.     }
  1181.     }
  1182.     if (NumTries >= kSCSIRetryValue) 
  1183.     {
  1184.     err = eReplyTOErr;
  1185.     }
  1186.     return (err);
  1187. }
  1188.  
  1189. static void ReadTime(void)
  1190. {
  1191.     long int t0;
  1192.     int track, startTrack, startMinutes, startSeconds, startFrames,
  1193.            endTrack,   endMinutes,   endSeconds,   endFrames;
  1194.     OSErr err;
  1195.  
  1196.     /* Enter format as track+min:sec:frame */
  1197.     printf("from? ");
  1198.     switch (scanf("%d+%d:%d:%d", &startTrack, &startMinutes,
  1199. &startSeconds, &startFrames))
  1200.     {
  1201.     case 0:
  1202.         startTrack = 1;
  1203.     case 1:
  1204.         startMinutes = 0;
  1205.     case 2:
  1206.         startSeconds = 0;
  1207.     case 3:
  1208.         startFrames = 0;
  1209.     }
  1210.     printf("to? ");
  1211.     switch (scanf("%d+%d:%d:%d", &endTrack, &endMinutes, &endSeconds,
  1212. &endFrames))
  1213.     {
  1214.     case 0:
  1215.         endTrack = 1;
  1216.     case 1:
  1217.         endMinutes = 0;
  1218.     case 2:
  1219.         endSeconds = 0;
  1220.     case 3:
  1221.         endFrames = 0;
  1222.     }
  1223.  
  1224.     startFrames += tracks[startTrack].frame;
  1225.     if (startFrames >= 75)
  1226.     {
  1227.     startSeconds += startFrames / 75;
  1228.     startFrames %= 75;
  1229.     }
  1230.     startSeconds += tracks[startTrack].second;
  1231.     if (startSeconds >= 60)
  1232.     {
  1233.     startMinutes += startSeconds / 60;
  1234.     startSeconds %= 60;
  1235.     }
  1236.     startMinutes += tracks[startTrack].minute;
  1237.     endFrames += tracks[endTrack].frame;
  1238.     if (endFrames >= 75)
  1239.     {
  1240.     endSeconds += endFrames / 75;
  1241.     endFrames %= 75;
  1242.     }
  1243.     endSeconds += tracks[endTrack].second;
  1244.     if (endSeconds >= 60)
  1245.     {
  1246.     endMinutes += endSeconds / 60;
  1247.     endSeconds %= 60;
  1248.     }
  1249.     endMinutes += tracks[endTrack].minute;
  1250.  
  1251.     bufSize = 2352L * (long int) ((long int) ((long int)
  1252.           (endMinutes - startMinutes) * 60 + endSeconds - startSeconds)
  1253.           * 75 + endFrames - startFrames);
  1254.     if ((data = malloc(bufSize)) == NULL)
  1255.     {
  1256.     perror("data");
  1257.     exit(1);
  1258.     }
  1259.  
  1260.     tib[0].scParam1 = (long int) data;
  1261.     tib[0].scParam2 = bufSize;
  1262.     readCommand1.startMinute = startMinutes;
  1263.     readCommand1.startSecond = startSeconds;
  1264.     readCommand1.startFrame = startFrames;
  1265.     readCommand1.endMinute = endMinutes;
  1266.     readCommand1.endSecond = endSeconds;
  1267.     readCommand1.endFrame = endFrames;
  1268.     t0 = TickCount();
  1269.     err = DoSCSIOutput(scsiID, sizeof(readCommand1), &readCommand1,
  1270.                tib, scsiReadXfer, 60 + bufSize / 2940);
  1271.     t0 = TickCount() - t0;
  1272.     if (err != noErr)
  1273.     {
  1274.     Signal(err);
  1275.     }
  1276.     printf("%ld bytes read in %g sec.\n", bufSize, t0 / 60.0);
  1277.     DumpBytes1(data, bufSize); /* Do something with it... */
  1278. }
  1279.